/*
    This source file is part of GeNa project.
    Copyright © GeNa project 2008
    For the latest info, see http://code.google.com/p/genaproject/ or  http://lumpyproject.forums-rpg.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>. */

#include "mesh.h"

#include <iostream>
#include <fstream>

Mesh::Mesh(domMesh* mesh)
{
    //Load each subMesh
    domLines_Array & lines = mesh->getLines_array();
    for( unsigned int i = 0 ; i < lines.getCount(); i++)
    {
        //Retrieve COLLADA data
        SubMesh* subMesh = new SubMesh;
        m_subMeshes.push_back(subMesh);
        subMesh->loadPrimitive(lines[i], GL_LINES);
    }

//    domLinestrips_Array & linestrips = mesh->getLinestrips_array();
//    for( unsigned int i = 0 ; i < linestrips.getCount(); i++)
//    {
//        //Retrieve COLLADA data
//        SubMesh* subMesh = new SubMesh;
//        m_subMeshes.push_back(subMesh);
//        subMesh->loadPrimitive(linestrips[i], GL_LINE_STRIP);
//    }

    domTriangles_Array & triangles = mesh->getTriangles_array();
    for( unsigned int i = 0 ; i < triangles.getCount(); i++)
    {
        //Retrieve COLLADA data
        SubMesh* subMesh = new SubMesh;
        m_subMeshes.push_back(subMesh);
        subMesh->loadPrimitive(triangles[i], GL_TRIANGLES);
    }

//    domTrifans_Array & trifans = mesh->getTrifans_array();
//    for( unsigned int i = 0 ; i < trifans.getCount(); i++)
//    {
//        //Retrieve COLLADA data
//        SubMesh* subMesh = new SubMesh;
//        m_subMeshes.push_back(subMesh);
//        subMesh->loadPrimitive(trifans[i], GL_TRIANGLE_FAN);
//    }

//    domTristrips_Array & tristrips = mesh->getTristrips_array();
//    for( unsigned int i = 0 ; i < tristrips.getCount(); i++)
//    {
//        //Retrieve COLLADA data
//        SubMesh* subMesh = new SubMesh;
//        m_subMeshes.push_back(subMesh);
//        subMesh->loadPrimitive(tristrips[i], GL_TRIANGLE_STRIP);
//    }

    //TODO : polygons and polylist
}

template<class T> void SubMesh::loadPrimitive( T & primitive, GLenum _renderingMode)
{
    //Assign material name
    materialName = primitive->getMaterial();

    //Read indices
    domListOfUInts & indexes = primitive->getP()->getValue();

    indexBuffer = new BufferObject(GL_ELEMENT_ARRAY_BUFFER, 4*indexes.getCount(), GL_DYNAMIC_DRAW);
    unsigned int* buf = (unsigned int*)indexBuffer->map(GL_WRITE_ONLY);

    unsigned int vertexMax = 0;
std::ofstream fout("out.dbg",std:: ios_base::app);
    fout << "elem\n";
    for( unsigned int j=0; j < indexes.getCount(); j++)
    {
        buf[j] = indexes[j];
        if( vertexMax < buf[j])
            vertexMax = buf[j];
        fout << buf[j] << " ";
    }
    fout.close();
    indexBuffer->unMap();
    count = indexes.getCount();
    indexType = GL_UNSIGNED_INT;

    //Operation mode
    renderingMode = _renderingMode;


    //Create and fill vertex declaration
    VertexDeclaration* decl = new VertexDeclaration;
    decls.push_back(decl);

    domInputLocalOffset_Array & inputArray = primitive->getInput_array();
    unsigned int index = 0;
    for( unsigned int j=0; j < inputArray.getCount(); j++)
        inputDecl( inputArray[j], index);
    decl->update();

    BufferObject* vb = new BufferObject(GL_ARRAY_BUFFER, vertexMax*decl->size, GL_DYNAMIC_DRAW);
    vertexBuffers.push_back(vb);
    index = 0;
    for( unsigned int j=0; j < inputArray.getCount(); j++)
        fillBuffer( inputArray[j], index);
}

template<class T, typename U > void SubMesh::fillBufferElem(domAccessor* accessor, unsigned int index,  T & array)
{
    VertexElement* elem = decls.front()->elems[index];
    BufferObject* buf = vertexBuffers.front();
    void* ptr = buf->map(GL_WRITE_ONLY);

    std::ofstream fout("out.dbg",std:: ios_base::app);
    fout << "array\n";
    for( unsigned int i = 0; i < array.getCount()/accessor->getStride(); i++)
    {
        U* u = elem->get<U>( i, ptr);

        domParam_Array & params = accessor->getParam_array();
        unsigned int component = 0;
        for( unsigned int k = 0; k < params.getCount(); k++)
        {
            if( params[k]->getName() != xsNCName(""))
            {
                u[component] = (U)array.getValue()[i* accessor->getStride() + k];
                component++;
                fout  << " " <<  array.getValue()[i* accessor->getStride() + k];
            }
        }
    }

    buf->unMap();
    fout.close();
    std::cout << std::endl;
}

template<class T> void SubMesh::fillBuffer(T input, unsigned int &index)
{
    //Read input
    daeElement* sourceDaeElem = input->getSource().getElement();

    //If input is a vertex declaration, read it.
    if( sourceDaeElem->typeID() == domVertices::ID())
    {
        domVertices* vertices = (domVertices*)sourceDaeElem;
        domInputLocal_Array vertArray = vertices->getInput_array();

        for( unsigned int i=0; i<vertArray.getCount();i++)
            fillBuffer( vertArray[i], index);
        return;
    }

    domSource* source = (domSource*)sourceDaeElem;
    domAccessor* accessor = source->getTechnique_common()->getAccessor();
    daeElement* daeArray = accessor->getSource().getElement();

    if( domBool_array::ID() == daeArray->typeID())
        fillBufferElem<domBool_array,short>(accessor, index, *(domBool_array*)daeArray);
    else if(domInt_array::ID() == daeArray->typeID())
        fillBufferElem<domInt_array,int>(accessor, index, *(domInt_array*)daeArray);
    else if(domFloat_array::ID() == daeArray->typeID())
        fillBufferElem<domFloat_array,float>(accessor, index, *(domFloat_array*)daeArray);
    else
        return;

    index++;
}


template<class T> void SubMesh::inputDecl(T input, unsigned int &index)
{
    //Read input
    daeElement* sourceDaeElem = input->getSource().getElement();

    //If input is a vertex declaration, read it.
    if( sourceDaeElem->typeID() == domVertices::ID())
    {
        domVertices* vertices = (domVertices*)sourceDaeElem;
        domInputLocal_Array vertArray = vertices->getInput_array();

        for( unsigned int i=0; i<vertArray.getCount();i++)
            inputDecl( vertArray[i], index);
        return;
    }

    //Else, create a declaration element from the source
    domSource* source = (domSource*)sourceDaeElem;

    VertexElement* elem = new VertexElement;

    elem->name = input->getSemantic();
    elem->pos = index;
    index++;
    elem->buffer = 0;
    elem->normalized = false;
    elem->numComponents = 0;

    domAccessor* accessor = source->getTechnique_common()->getAccessor();
    daeElement* daeArray = accessor->getSource().getElement();

    if( domBool_array::ID() == daeArray->typeID())
        elem->type = GL_SHORT;
    else if(domInt_array::ID() == daeArray->typeID())
        elem->type = GL_INT;
    else if(domFloat_array::ID() == daeArray->typeID())
        elem->type = GL_FLOAT;
    else
    {
        std::cout << "Error Name and IDREF sources not accepted";
        return;
    }

    domParam_Array & array = accessor->getParam_array();
    for( unsigned int k = 0; k < array.getCount(); k++)
        if( array[k]->getName() != xsNCName(""))
            elem->numComponents++;

    namedElements[ elem->name] = elem;
    decls.front()->elems.push_back(elem);
}
